 /*
      *******************************************************************************
      * @file       queuef.c
      * @version    V1.13    
      * @date       2010.04.26
      * @brief  FIFO-based queue management implementation code of CooCox CoOS kernel.	
      *******************************************************************************
      * @copy
      *
      * INTERNAL FILE,DON'T PUBLIC.
      * 
      * ? COPYRIGHT 2009 CooCox 
      *******************************************************************************
      */ 
     
     /*---------------------------- Include ---------------------------------------*/
     //#include "coos.h"
#include <coocox.h>

#define CFG_QF_EN    1

typedef struct QueueF
{
	void    *qfStart;                 	/*!< pointer to start of FIFO storage */
	U8      id;                         /*!<                                  */
	U8 		MessageCount;				//amount of stored messages
	U8		nFirst;                       /*!< first (oldest) element              */
	U8		nLast;                       /*!< The end of queue                 */
	U8		MaxMessages;                   U8     nEndOfStorage;               //number of last element in array
	unsigned ReadAck:	1;				//indicates, that data was read by software
}QFCB,*P_QFCB;

 #if CFG_QF_EN > 0
 /*---------------------------- Variable Define -------------------------------*/
QFCB	qfTbl[CFG_MAX_QF] = {{0}};    /*!< qf control block table        */
U32		qfIDVessel = 0;                /*!< qf list mask                  */



 /**
  *******************************************************************************
  * @brief      Create a qf
  * @param[in]  qfStart  Pointer to storage for both index and data.
  * @param[in]  StorageSize      Size of storage (bytes).
  * @param[in]  MaxMessages      max number of entries in mailbox.
  * @param[in]  sortType  Mail queue waiting list sort type.
  * @param[out] None
  * @retval     E_CREATE_FAIL  Create qf fail.
  * @retval     others         Create qf successful.
  *
  * @par Description
  * @details    This function is called to create a qf.
  * @note
  start of storage is taken by U16 indexes
  index = (U8 lenth<<8 | U8 startByte)
  *******************************************************************************
  */
OS_EventID CoCreateQF(void *qfStart, U16 StorageSize, U16 MaxMessages,U8 sortType)
 {
	U8    i;
	P_ECB pecb;
	U16 * p16;

 #if CFG_PAR_CHECKOUT_EN >0
	//abort if no pointer specified, of nothing except indexes can fit storage
	 if((qfStart == NULL) || (StorageSize <= MaxMessages * 2))
	 {
		 return E_CREATE_FAIL;
	 }
 #endif

	OsSchedLock();
	 for(i = 0; i < CFG_MAX_QF; i++)
	 {
		 /* Assign a free qf control block                                  */
		 if((qfIDVessel & (1 << i)) == 0)
		 {
			qfIDVessel |= (1<<i);
			OsSchedUnlock();

			qfTbl[i].qfStart   = qfStart;  /* Initialize the qf           */
			qfTbl[i].MessageCount = 0;
			qfTbl[i].id       = i;
			qfTbl[i].nFirst     = 0;
			qfTbl[i].nLast     = 0;
			qfTbl[i].MaxMessages = MaxMessages;
			qfTbl[i].nEndOfStorage = StorageSize - 1;
			qfTbl[i].ReadAck 		= 1;

			p16 = qfStart;
			*p16 = (MaxMessages << 1);//set pointer to data part

			 /* Get a event control block and initial the event content        */
			pecb = CreatEvent(EVENT_TYPE_QF,sortType,&qfTbl[i]);

			 if(pecb == NULL )       /* If there is no free EVENT control block*/
			 {
				 return E_CREATE_FAIL;
			 }
			 return (pecb->id);
		 }
	 }

	OsSchedUnlock();
	 return E_CREATE_FAIL;             /* There is no free qf control block */
 }


 /**
  *******************************************************************************
  * @brief      Delete a qf
  * @param[in]  id     Event ID.
  * @param[in]  opt    Delete option.
  * @param[out] None
  * @retval     E_INVALID_ID         Invalid event ID.
  * @retval     E_INVALID_PARAMETER  Invalid parameter.
  * @retval     E_TASK_WAITTING      Tasks waitting for the event,delete fail.
  * @retval     E_OK                 Event deleted successful.
  *
  * @par Description
  * @details    This function is called to delete a qf.
  * @note
  *******************************************************************************
  */
StatusType CoDelqf(OS_EventID id,U8 opt)
 {
	P_ECB   pecb;
	P_QFCB   pqfcb;
	StatusType err;
 #if CFG_PAR_CHECKOUT_EN >0
	 if(id >= CFG_MAX_EVENT)
	 {
		 return E_INVALID_ID;            /* Invalid id,return error            */
	 }
 #endif

	pecb = &EventTbl[id];
 #if CFG_PAR_CHECKOUT_EN >0
	 if( pecb->eventType != EVENT_TYPE_QF)
	 {
		 return E_INVALID_ID;            /* The event is not qf,return error*/
	 }
 #endif
	 pqfcb = (P_QFCB)pecb->eventPtr;       /* Point at qf control block       */
	err  = DeleteEvent(pecb,opt);       /* Delete the event control block     */
	 if(err == E_OK)                   /* If the event block have been deleted */
	 {
		qfIDVessel &= ~((U32)(1<<(pqfcb->id)));   /* Update free qf list             */
		pqfcb->qfStart   = NULL;
		pqfcb->MessageCount = 0;
		pqfcb->id       = 0;
		pqfcb->nFirst     = 0;
		pqfcb->nLast     = 0;
		pqfcb->MaxMessages = 0;
		pqfcb->nEndOfStorage    = 0;
		pqfcb->ReadAck = 0;
	 }
	 return err;
 }



 /**
  *******************************************************************************
  * @brief      Accept a mail from qf
  * @param[in]  id     Event ID.
  * @param[out] lenth  A pointer to message lenth
  * @param[out] perr   A pointer to error code.
  * @retval     NULL
  * @retval     A pointer to mail accepted.
  *
  * @par Description
  * @details    This function is called to accept a mail from qf.
  * @note
  *******************************************************************************
  */
 void* CoAcceptqfMail(OS_EventID id, U8 * lenth, StatusType* perr)
 {
	P_ECB pecb;
	P_QFCB pqfcb;
	U8 * pData;
	void * pStart;
	U32 lnFirst;//local nFirst

 #if CFG_PAR_CHECKOUT_EN >0
	 if(id >= CFG_MAX_EVENT)
	 {
		 *perr = E_INVALID_ID;           /* Invalid id,return error            */
		 return NULL;
	 }
 #endif

	pecb = &EventTbl[id];
 #if CFG_PAR_CHECKOUT_EN >0
	 if(pecb->eventType != EVENT_TYPE_QF)/* Invalid event control block type*/
	 {
		 *perr = E_INVALID_ID;
		 return NULL;
	 }
 #endif
	pqfcb = (P_QFCB)pecb->eventPtr;       /* Point at qf control block       */
	OsSchedLock();
	 if(pqfcb->MessageCount != 0)            /* If there are any messages in the qf */
	 {
		pStart = pqfcb->qfStart;
		pData = pStart;
		OsSchedLock();
		lnFirst = pqfcb->nFirst;
 #if CFG_PAR_CHECKOUT_EN >0
		 if (!pqfcb->ReadAck) {//if acknowledge is missing
			lnFirst++;
			//increase Last number, check for out-of-array condition
			pqfcb->nFirst = lnFirst >= pqfcb->MaxMessages ? 0 : lnFirst;
			pqfcb->MessageCount--;          /* Update the number of messages in the qf */
		} else {
			*perr = E_OK;
		}
 #else
		*perr = E_OK;
 #endif
		pqfcb->ReadAck = 0;
		pData += lnFirst << 1; //make pointer to current index
		*lenth = *pData++;//unpack lenth
		pData = (U8 *) pStart + *pData;//unpack pointer to data itself
		OsSchedUnlock();

		 return (void *) pData;                   /* Return message received            */
	 }
	 else                                /* If there is no message in the qf*/
	 {
		OsSchedUnlock();
		 *perr = E_QF_EMPTY;
		 return NULL;                    /* Return NULL                        */
	 }
 }




 /**
  *******************************************************************************
  * @brief      Post a mail to qf
  * @param[in]  id      Event ID.
  * @param[in]  pmail   Pointer to mail that want to send.
  * @param[out] None
  * @retval     E_OK
  * @retval     E_INVALID_ID
  * @retval     E_qf_FULL
  *
  * @par Description
  * @details    This function is called to post a mail to qf.
  * @note
  *******************************************************************************
  */
StatusType CoPostqfMail(OS_EventID id,void* pmail, U8 size)
 {
	P_ECB pecb;
	P_QFCB pqfcb;
	U32 nDataStart;
	S32 ndFirst;
	S32 ndLast;
	S32 * nlLast;
	U8 * pData8;
	U32 * pData32;//32-bit pointer
 #if CFG_PAR_CHECKOUT_EN >0
	 if(id >= CFG_MAX_EVENT)
	 {
		 return E_INVALID_ID;
	 }
 #endif

	pecb = &EventTbl[id];
 #if CFG_PAR_CHECKOUT_EN >0
	 if(pecb->eventType != EVENT_TYPE_QF)
	 {
		 return E_INVALID_ID;            /* The event type isn't qf,return  */
	 }
 #endif
	 pqfcb = (P_QFCB)pecb->eventPtr;
	 if(pqfcb->MessageCount == pqfcb->MaxMessages)   /* If qf is full                   */
	 {
		 return E_QF_FULL;
	 }
	 else                                /* If qf is not full               */
	 {
		pData8 = pqfcb->qfStart;
		nDataStart = pqfcb->MaxMessages << 1;
		OsSchedLock();
		//unpack numbers of data (nd)
		ndFirst = *(pData8 + (pqfcb->nFirst << 1));
		ndLast = *(pData8 + (pqfcb->nLast << 1));
		//check for free space
		if (ndLast < ndFirst) {//if write will occur between last and first
			if (ndFirst - ndLast <= size) {//if there's no enough space
				return E_QF_FULL;
			}
		}
		else {
			if (pqfcb->nEndOfStorage - ndLast <= size) {//check for space from last to end of storage
				if (nDataStart - ndFirst <= size) {//check for space from start of storage to first
					return E_QF_FULL;
				} else {
					ndLast = nDataStart;
				}
			}
		}
		//ndLast - number of new data start
		nlLast = pqfcb->nLast + 1;
		//increase Last number, check for out-of-array condition
		pqfcb->nLast = nlLast >= pqfcb->MaxMessages ? 0 : nlLast;
		*(pData8 + (pqfcb->nLast << 1)) = ndLast | size << 8;
			  pData32 = (U32 *) (pData8 + ndLast);
		size -= 4;
		while (size > 0) {
			*((U32 *) pmail)++ = *pData32++;
			size -= 4;
		}
		size += 4;
		while (size > 0) {
			*((U8 *) pmail)++ = *((U8 *) pData32)++;
			size -= 1;
		}

		EventTaskToRdy(pecb);           /* Check the event waiting list       */
		OsSchedUnlock();
		 return E_OK;
	 }
 }


 /*
  *******************************************************************************
  * @brief      Acknowledge data read by user software
  * @param[in]  id      Event ID.
  * @param[out] None
  * @retval     E_OK
  * @retval     E_INVALID_ID
  * @retval     E_QF_ACC_ERROR
  *
  * @par Description
  * @details    This function is called by user software to indicate, that last
  * @details    data, returned by GetMail was read.
  * @note
  *******************************************************************************
  */
OS_EventID CoQFReadAck (OS_EventID id) {
	P_ECB   pecb;
	P_QFCB   pqfcb;
	U32 lnFirst;
 #if CFG_PAR_CHECKOUT_EN >0
	 if(id >= CFG_MAX_EVENT)
	 {
		 return E_INVALID_ID;
	 }
 #endif
	pecb = &EventTbl[id];
 #if CFG_PAR_CHECKOUT_EN >0
	 if( pecb->eventType != EVENT_TYPE_QF)
	 {
		 return E_INVALID_ID;            /* The event is not qf,return error*/
	 }
 #endif
	 pqfcb = (P_QFCB)pecb->eventPtr;       /* Point at qf control block       */

 #if CFG_PAR_CHECKOUT_EN >0
	if (!pqfcb->ReadAck) {
 #endif
		lnFirst = pqfcb->nFirst;
		lnFirst++;
		//increase Last number, check for out-of-array condition
		pqfcb->nFirst = lnFirst >= pqfcb->MaxMessages ? 0 : lnFirst;
		pqfcb->MessageCount--;          /* Update the number of messages in the qf */
 #if CFG_PAR_CHECKOUT_EN >0
	} else {
		return E_QF_ACC_ERROR;
	}
 #endif
	return E_OK;
 }

 #endif //this is the data struct for fifo-based queuetypedef


